import torch
from torch import  nn
from d2l import torch as d2l

print(torch.cuda.is_available())

# # AlexNet
# net = nn.Sequential(
#     # 这里使用一个11*11的更大窗口来捕捉对象。
#     # 同时，步幅为4，以减少输出的高度和宽度。
#     # 另外，输出通道的数目远大于LeNet
#     nn.Conv2d(1, 96, kernel_size=11, stride=4, padding=1), nn.ReLU(),
#     nn.MaxPool2d(kernel_size=3, stride=2),
#     # 减小卷积窗口，使用填充为2来使得输入与输出的高和宽一致，且增大输出通道数
#     nn.Conv2d(96, 256, kernel_size=5, padding=2), nn.ReLU(),
#     nn.MaxPool2d(kernel_size=3, stride=2),
#     # 使用三个连续的卷积层和较小的卷积窗口。
#     # 除了最后的卷积层，输出通道的数量进一步增加。
#     # 在前两个卷积层之后，汇聚层不用于减少输入的高度和宽度
#     nn.Conv2d(256, 384, kernel_size=3, padding=1), nn.ReLU(),
#     nn.Conv2d(384, 384, kernel_size=3, padding=1), nn.ReLU(),
#     nn.Conv2d(384, 256, kernel_size=3, padding=1), nn.ReLU(),
#     nn.MaxPool2d(kernel_size=3, stride=2),
#     nn.Flatten(),
#     # 这里，全连接层的输出数量是LeNet中的好几倍。使用dropout层来减轻过拟合
#     nn.Linear(6400, 4096), nn.ReLU(),
#     nn.Dropout(p=0.5),
#     nn.Linear(4096, 4096), nn.ReLU(),
#     nn.Dropout(p=0.5),
#     # 最后是输出层。由于这里使用Fashion-MNIST，所以用类别数为10，而非论文中的1000
#     nn.Linear(4096, 10))
#
# # X = torch.rand(size=(1,1,224,224),dtype=torch.float32)
# # for layer in net:
# #     X = layer(X)
# #     print(f"{layer.__class__.__name__} output shape:{X.shape}")
#
# batch_size = 128
# train_iter,test_iter = d2l.load_data_fashion_mnist(batch_size=batch_size,resize=224)
#
# lr , num_epochs = 0.1,10
# d2l.train_ch6(net,train_iter,test_iter,num_epochs,lr,d2l.try_gpu())
# d2l.plt.show()

# VGG的实现

def vgg_block(num_convs,in_channels,out_channels):
    layers = []
    for _ in range(num_convs):
        layers.append(nn.Conv2d(
            in_channels,out_channels,kernel_size=3,padding=1))
        layers.append(nn.ReLU())
        in_channels = out_channels
    layers.append(nn.MaxPool2d(kernel_size=2,stride=2))
    return nn.Sequential(*layers)

conv_arch = ((1,64),(1,128),(2,256),(2,512),(2,512))

def vgg(conv_arch):
    conv_blks = []
    in_channels = 1
    for (num_convs,out_channels) in conv_arch:
        conv_blks.append(vgg_block(num_convs,in_channels,out_channels))
        in_channels = out_channels

    return nn.Sequential(*conv_blks,nn.Flatten()  ,
                        nn.Linear(in_features=out_channels*7*7,out_features=4096),nn.ReLU(),
                         nn.Dropout(0.5),nn.Linear(4096,4096),nn.ReLU(),
                         nn.Dropout(0.5),nn.Linear(4096,10))

# net = vgg(conv_arch)

# X = torch.randn(size=(1, 1, 224, 224))
# for blk in net:
#     X = blk(X)
#     print(blk.__class__.__name__,'output shape:\t',X.shape)

ratio = 4
small_conv_arch = [ (pair[0],pair[1]//ratio) for pair in conv_arch ]
net = vgg(small_conv_arch)

lr ,num_epochs = 0.05,10
batch_size = 32
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size,resize=224)
d2l.train_ch6(net,train_iter,test_iter,num_epochs,lr,torch.device(f'cuda:{0}'))